package org.bjf.config;


import com.alibaba.fastjson.JSON;
import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.HttpHost;
import org.apache.http.client.HttpClient;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

/**
 * @author bjf
 * @see https://blog.csdn.net/zzzgd_666/article/details/88858181
 */
@Configuration
@Slf4j
public class RestConfig {

  /**
   * 针对不同的网址,长连接保持的存活时间,单位s
   */
  private Map<String, Integer> keepAliveTargetHost = ImmutableMap.of("http://www.baidu.com", 5);

  @Bean
  public RestTemplate restTemplate(HttpComponentsClientHttpRequestFactory factory) {
    return new RestTemplate(factory);
  }

  @Bean
  public HttpComponentsClientHttpRequestFactory clientHttpRequestFactory(HttpClient httpClient) {
    HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
    factory.setConnectTimeout(3000);
    factory.setReadTimeout(5000);
    return factory;

  }

  @Bean
  public HttpClient httpClient() throws Exception {
    //===1.设置信任ssl访问
    SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (arg0, arg1) -> true)
        .build();

    //===2.Registry配置
    HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
    SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
        sslContext, hostnameVerifier);
    Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
        //注册http和https请求
        .register("http", PlainConnectionSocketFactory.getSocketFactory())
        .register("https", sslConnectionSocketFactory).build();

    //===3.连接池配置(推荐)，同时支持netty，okHttp以及其他http框架
    PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(
        socketFactoryRegistry);
    // 最大连接数
    cm.setMaxTotal(1000);
    // 同路由并发数,默认值是2
    cm.setDefaultMaxPerRoute(200);
    //剔除过期无效连接
    cm.closeExpiredConnections();
    cm.setValidateAfterInactivity(2000);

    //===4.设置httpClient
    HttpClientBuilder hc = HttpClientBuilder.create();
    hc.useSystemProperties()
        //设置信任ssl访问
        .setSSLContext(sslContext)
        //配置连接池
        .setConnectionManager(cm);
    // 重试次数
//          .setRetryHandler(
//              new DefaultHttpRequestRetryHandler(2, true));

    //设置默认请求头
    List<Header> headers = getDefaultHeaders();
    hc.setDefaultHeaders(headers);
    //设置长连接保持策略
//    hc.setKeepAliveStrategy(connectionKeepAliveStrategy());
    return hc.build();
  }


  /**
   * 配置长连接保持策略
   */
  private ConnectionKeepAliveStrategy connectionKeepAliveStrategy() {
    return (response, context) -> {
      // Honor 'keep-alive' header
      HeaderElementIterator it = new BasicHeaderElementIterator(
          response.headerIterator(HTTP.CONN_KEEP_ALIVE));
      while (it.hasNext()) {
        HeaderElement he = it.nextElement();
        log.info("HeaderElement:{}", JSON.toJSONString(he));
        String param = he.getName();
        String value = he.getValue();
        if (value != null && "timeout".equalsIgnoreCase(param)) {
          try {
            return Long.parseLong(value) * 1000;
          } catch (NumberFormatException ignore) {
            log.error("解析长连接过期时间异常", ignore);
          }
        }
      }
      HttpHost target = (HttpHost) context.getAttribute(
          HttpClientContext.HTTP_TARGET_HOST);
      //如果请求目标地址,单独配置了长连接保持时间,使用该配置
      Optional<Entry<String, Integer>> any = keepAliveTargetHost
          .entrySet().stream().filter(
              e -> e.getKey().equalsIgnoreCase(target.getHostName())).findAny();
      //否则使用默认长连接保持时间
      return any.map(en -> en.getValue() * 1000L)
          .orElse(10 * 1000L);
    };
  }


  /**
   * 设置请求头
   */
  private List<Header> getDefaultHeaders() {
    List<Header> headers = new ArrayList<>();
    headers.add(new BasicHeader("User-Agent",
        "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.16 Safari/537.36"));
    headers.add(new BasicHeader("Accept-Encoding", "gzip,deflate"));
    headers.add(new BasicHeader("Accept-Language", "zh-CN"));
    headers.add(new BasicHeader("Connection", "Keep-Alive"));
    return headers;
  }

}
